﻿using System;

public class CpuInfo
{
    public const int ADDR_ABS = 3;
    public const int ADDR_ABSX = 8;
    public const int ADDR_ABSY = 9;
    public const int ADDR_ACC = 4;
    public const int ADDR_IMM = 5;
    public const int ADDR_IMP = 2;
    public const int ADDR_INDABS = 12;
    public const int ADDR_POSTIDXIND = 11;
    public const int ADDR_PREIDXIND = 10;
    public const int ADDR_REL = 1;
    public const int ADDR_ZP = 0;
    public const int ADDR_ZPX = 6;
    public const int ADDR_ZPY = 7;
    private static string[] addrDesc;
    public static int[] cycTable;
    public const int INS_ADC = 0;
    public const int INS_AND = 1;
    public const int INS_ASL = 2;
    public const int INS_BCC = 3;
    public const int INS_BCS = 4;
    public const int INS_BEQ = 5;
    public const int INS_BIT = 6;
    public const int INS_BMI = 7;
    public const int INS_BNE = 8;
    public const int INS_BPL = 9;
    public const int INS_BRK = 10;
    public const int INS_BVC = 11;
    public const int INS_BVS = 12;
    public const int INS_CLC = 13;
    public const int INS_CLD = 14;
    public const int INS_CLI = 15;
    public const int INS_CLV = 0x10;
    public const int INS_CMP = 0x11;
    public const int INS_CPX = 0x12;
    public const int INS_CPY = 0x13;
    public const int INS_DEC = 20;
    public const int INS_DEX = 0x15;
    public const int INS_DEY = 0x16;
    public const int INS_DUMMY = 0x38;
    public const int INS_EOR = 0x17;
    public const int INS_INC = 0x18;
    public const int INS_INX = 0x19;
    public const int INS_INY = 0x1a;
    public const int INS_JMP = 0x1b;
    public const int INS_JSR = 0x1c;
    public const int INS_LDA = 0x1d;
    public const int INS_LDX = 30;
    public const int INS_LDY = 0x1f;
    public const int INS_LSR = 0x20;
    public const int INS_NOP = 0x21;
    public const int INS_ORA = 0x22;
    public const int INS_PHA = 0x23;
    public const int INS_PHP = 0x24;
    public const int INS_PLA = 0x25;
    public const int INS_PLP = 0x26;
    public const int INS_ROL = 0x27;
    public const int INS_ROR = 40;
    public const int INS_RTI = 0x29;
    public const int INS_RTS = 0x2a;
    public const int INS_SBC = 0x2b;
    public const int INS_SEC = 0x2c;
    public const int INS_SED = 0x2d;
    public const int INS_SEI = 0x2e;
    public const int INS_STA = 0x2f;
    public const int INS_STX = 0x30;
    public const int INS_STY = 0x31;
    public const int INS_TAX = 50;
    public const int INS_TAY = 0x33;
    public const int INS_TSX = 0x34;
    public const int INS_TXA = 0x35;
    public const int INS_TXS = 0x36;
    public const int INS_TYA = 0x37;
    private static string[] instname;
    private static int[] opdata;

    public static string getAddressModeName(int addrMode)
    {
        if (addrDesc == null)
        {
            initAddrDesc();
        }
        if ((addrMode >= 0) && (addrMode < addrDesc.Length))
        {
            return addrDesc[addrMode];
        }
        return "???";
    }

    public static string getInstName(int inst)
    {
        if (instname == null)
        {
            initInstNames();
        }
        if (inst < instname.Length)
        {
            return instname[inst];
        }
        return "???";
    }

    private static void initAddrDesc()
    {
        addrDesc = new string[] { "Zero Page           ", "Relative            ", "Implied             ", "Absolute            ", "Accumulator         ", "Immediate           ", "Zero Page,X         ", "Zero Page,Y         ", "Absolute,X          ", "Absolute,Y          ", "Preindexed Indirect ", "Postindexed Indirect", "Indirect Absolute   " };
    }

    private static void initInstNames()
    {
        instname = new string[] { 
            "ADC", "AND", "ASL", "BCC", "BCS", "BEQ", "BIT", "BMI", "BNE", "BPL", "BRK", "BVC", "BVS", "CLC", "CLD", "CLI", 
            "CLV", "CMP", "CPX", "CPY", "DEC", "DEX", "DEY", "EOR", "INC", "INX", "INY", "JMP", "JSR", "LDA", "LDX", "LDY", 
            "LSR", "NOP", "ORA", "PHA", "PHP", "PLA", "PLP", "ROL", "ROR", "RTI", "RTS", "SBC", "SEC", "SED", "SEI", "STA", 
            "STX", "STY", "TAX", "TAY", "TSX", "TXA", "TXS", "TYA"
         };
    }

    private static void initOpData()
    {
        opdata = new int[0x100];
        for (int i = 0; i < 0x100; i++)
        {
            opdata[i] = 0xff;
        }
        setOp(0, 0x69, 5, 2, 2);
        setOp(0, 0x65, 0, 2, 3);
        setOp(0, 0x75, 6, 2, 4);
        setOp(0, 0x6d, 3, 3, 4);
        setOp(0, 0x7d, 8, 3, 4);
        setOp(0, 0x79, 9, 3, 4);
        setOp(0, 0x61, 10, 2, 6);
        setOp(0, 0x71, 11, 2, 5);
        setOp(1, 0x29, 5, 2, 2);
        setOp(1, 0x25, 0, 2, 3);
        setOp(1, 0x35, 6, 2, 4);
        setOp(1, 0x2d, 3, 3, 4);
        setOp(1, 0x3d, 8, 3, 4);
        setOp(1, 0x39, 9, 3, 4);
        setOp(1, 0x21, 10, 2, 6);
        setOp(1, 0x31, 11, 2, 5);
        setOp(2, 10, 4, 1, 2);
        setOp(2, 6, 0, 2, 5);
        setOp(2, 0x16, 6, 2, 6);
        setOp(2, 14, 3, 3, 6);
        setOp(2, 30, 8, 3, 7);
        setOp(3, 0x90, 1, 2, 2);
        setOp(4, 0xb0, 1, 2, 2);
        setOp(5, 240, 1, 2, 2);
        setOp(6, 0x24, 0, 2, 3);
        setOp(6, 0x2c, 3, 3, 4);
        setOp(7, 0x30, 1, 2, 2);
        setOp(8, 0xd0, 1, 2, 2);
        setOp(9, 0x10, 1, 2, 2);
        setOp(10, 0, 2, 1, 7);
        setOp(11, 80, 1, 2, 2);
        setOp(12, 0x70, 1, 2, 2);
        setOp(13, 0x18, 2, 1, 2);
        setOp(14, 0xd8, 2, 1, 2);
        setOp(15, 0x58, 2, 1, 2);
        setOp(0x10, 0xb8, 2, 1, 2);
        setOp(0x11, 0xc9, 5, 2, 2);
        setOp(0x11, 0xc5, 0, 2, 3);
        setOp(0x11, 0xd5, 6, 2, 4);
        setOp(0x11, 0xcd, 3, 3, 4);
        setOp(0x11, 0xdd, 8, 3, 4);
        setOp(0x11, 0xd9, 9, 3, 4);
        setOp(0x11, 0xc1, 10, 2, 6);
        setOp(0x11, 0xd1, 11, 2, 5);
        setOp(0x12, 0xe0, 5, 2, 2);
        setOp(0x12, 0xe4, 0, 2, 3);
        setOp(0x12, 0xec, 3, 3, 4);
        setOp(0x13, 0xc0, 5, 2, 2);
        setOp(0x13, 0xc4, 0, 2, 3);
        setOp(0x13, 0xcc, 3, 3, 4);
        setOp(20, 0xc6, 0, 2, 5);
        setOp(20, 0xd6, 6, 2, 6);
        setOp(20, 0xce, 3, 3, 6);
        setOp(20, 0xde, 8, 3, 7);
        setOp(0x15, 0xca, 2, 1, 2);
        setOp(0x16, 0x88, 2, 1, 2);
        setOp(0x17, 0x49, 5, 2, 2);
        setOp(0x17, 0x45, 0, 2, 3);
        setOp(0x17, 0x55, 6, 2, 4);
        setOp(0x17, 0x4d, 3, 3, 4);
        setOp(0x17, 0x5d, 8, 3, 4);
        setOp(0x17, 0x59, 9, 3, 4);
        setOp(0x17, 0x41, 10, 2, 6);
        setOp(0x17, 0x51, 11, 2, 5);
        setOp(0x18, 230, 0, 2, 5);
        setOp(0x18, 0xf6, 6, 2, 6);
        setOp(0x18, 0xee, 3, 3, 6);
        setOp(0x18, 0xfe, 8, 3, 7);
        setOp(0x19, 0xe8, 2, 1, 2);
        setOp(0x1a, 200, 2, 1, 2);
        setOp(0x1b, 0x4c, 3, 3, 3);
        setOp(0x1b, 0x6c, 12, 3, 5);
        setOp(0x1c, 0x20, 3, 3, 6);
        setOp(0x1d, 0xa9, 5, 2, 2);
        setOp(0x1d, 0xa5, 0, 2, 3);
        setOp(0x1d, 0xb5, 6, 2, 4);
        setOp(0x1d, 0xad, 3, 3, 4);
        setOp(0x1d, 0xbd, 8, 3, 4);
        setOp(0x1d, 0xb9, 9, 3, 4);
        setOp(0x1d, 0xa1, 10, 2, 6);
        setOp(0x1d, 0xb1, 11, 2, 5);
        setOp(30, 0xa2, 5, 2, 2);
        setOp(30, 0xa6, 0, 2, 3);
        setOp(30, 0xb6, 7, 2, 4);
        setOp(30, 0xae, 3, 3, 4);
        setOp(30, 190, 9, 3, 4);
        setOp(0x1f, 160, 5, 2, 2);
        setOp(0x1f, 0xa4, 0, 2, 3);
        setOp(0x1f, 180, 6, 2, 4);
        setOp(0x1f, 0xac, 3, 3, 4);
        setOp(0x1f, 0xbc, 8, 3, 4);
        setOp(0x20, 0x4a, 4, 1, 2);
        setOp(0x20, 70, 0, 2, 5);
        setOp(0x20, 0x56, 6, 2, 6);
        setOp(0x20, 0x4e, 3, 3, 6);
        setOp(0x20, 0x5e, 8, 3, 7);
        setOp(0x21, 0xea, 2, 1, 2);
        setOp(0x22, 9, 5, 2, 2);
        setOp(0x22, 5, 0, 2, 3);
        setOp(0x22, 0x15, 6, 2, 4);
        setOp(0x22, 13, 3, 3, 4);
        setOp(0x22, 0x1d, 8, 3, 4);
        setOp(0x22, 0x19, 9, 3, 4);
        setOp(0x22, 1, 10, 2, 6);
        setOp(0x22, 0x11, 11, 2, 5);
        setOp(0x23, 0x48, 2, 1, 3);
        setOp(0x24, 8, 2, 1, 3);
        setOp(0x25, 0x68, 2, 1, 4);
        setOp(0x26, 40, 2, 1, 4);
        setOp(0x27, 0x2a, 4, 1, 2);
        setOp(0x27, 0x26, 0, 2, 5);
        setOp(0x27, 0x36, 6, 2, 6);
        setOp(0x27, 0x2e, 3, 3, 6);
        setOp(0x27, 0x3e, 8, 3, 7);
        setOp(40, 0x6a, 4, 1, 2);
        setOp(40, 0x66, 0, 2, 5);
        setOp(40, 0x76, 6, 2, 6);
        setOp(40, 110, 3, 3, 6);
        setOp(40, 0x7e, 8, 3, 7);
        setOp(0x29, 0x40, 2, 1, 6);
        setOp(0x2a, 0x60, 2, 1, 6);
        setOp(0x2b, 0xe9, 5, 2, 2);
        setOp(0x2b, 0xe5, 0, 2, 3);
        setOp(0x2b, 0xf5, 6, 2, 4);
        setOp(0x2b, 0xed, 3, 3, 4);
        setOp(0x2b, 0xfd, 8, 3, 4);
        setOp(0x2b, 0xf9, 9, 3, 4);
        setOp(0x2b, 0xe1, 10, 2, 6);
        setOp(0x2b, 0xf1, 11, 2, 5);
        setOp(0x2c, 0x38, 2, 1, 2);
        setOp(0x2d, 0xf8, 2, 1, 2);
        setOp(0x2e, 120, 2, 1, 2);
        setOp(0x2f, 0x85, 0, 2, 3);
        setOp(0x2f, 0x95, 6, 2, 4);
        setOp(0x2f, 0x8d, 3, 3, 4);
        setOp(0x2f, 0x9d, 8, 3, 5);
        setOp(0x2f, 0x99, 9, 3, 5);
        setOp(0x2f, 0x81, 10, 2, 6);
        setOp(0x2f, 0x91, 11, 2, 6);
        setOp(0x30, 0x86, 0, 2, 3);
        setOp(0x30, 150, 7, 2, 4);
        setOp(0x30, 0x8e, 3, 3, 4);
        setOp(0x31, 0x84, 0, 2, 3);
        setOp(0x31, 0x94, 6, 2, 4);
        setOp(0x31, 140, 3, 3, 4);
        setOp(50, 170, 2, 1, 2);
        setOp(0x33, 0xa8, 2, 1, 2);
        setOp(0x34, 0xba, 2, 1, 2);
        setOp(0x35, 0x8a, 2, 1, 2);
        setOp(0x36, 0x9a, 2, 1, 2);
        setOp(0x37, 0x98, 2, 1, 2);
        cycTable = new int[] { 
            7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 
            6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 
            6, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 
            6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 
            2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 
            2, 6, 2, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, 
            2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, 
            2, 5, 2, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, 
            2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, 
            2, 6, 3, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, 
            2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7
         };
    }

    private static void setOp(int inst, int op, int addr, int size, int cycles)
    {
        opdata[op] = (((inst & 0xff) | ((addr & 0xff) << 8)) | ((size & 0xff) << 0x10)) | ((cycles & 0xff) << 0x18);
    }

    public static string[] AddressModeNames
    {
        get
        {
            if (addrDesc == null)
            {
                initAddrDesc();
            }
            return addrDesc;
        }
    }

    public static string[] InstNames
    {
        get
        {
            if (instname == null)
            {
                initInstNames();
            }
            return instname;
        }
    }

    public static int[] OpData
    {
        get
        {
            if (opdata == null)
            {
                initOpData();
            }
            return opdata;
        }
    }
}

